package main;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Timer;
import java.util.TimerTask;

import commandhandlers.BotHandler;
import commandhandlers.UserHandler;

public class UserCentral {
   /**
    * A class that represents an authenticated user
    * 
    * 
    */
   public static class AuthenticatedUser {
	  private final String mainNick;
	  private final boolean isAdmin;

	  public AuthenticatedUser(final String mainNick, final boolean isAdmin) {
		 this.mainNick = mainNick;
		 this.isAdmin = isAdmin;
	  }

	  public String getMainNick() {
		 return mainNick;
	  }

	  public boolean isAdmin() {
		 return isAdmin;
	  }
   }

   /**
    * Timer task that removes a check for auto nick linking
    * 
    * 
    */
   private class NickChangeTask extends TimerTask {
	  private final String user;

	  public NickChangeTask(final String user) {
		 super();
		 this.user = user;
	  }

	  @Override
	  public void run() {
		 autoNickLinkCheck.remove(user);
	  }
   }

   /**
    * Used to save user modes/prefixes
    */
   public static class UserPrefixes {
	  public boolean isOwner = false;
	  public boolean isSop = false;
	  public boolean isOp = false;
	  public boolean isHalfOp = false;
	  public boolean hasVoice = false;
   }

   private final List<String> mainchanUserList;
   private final Map<String, UserPrefixes> mainchanUserPrefixes;

   private final List<String> adminchanUserList;

   private final Map<String, AuthenticatedUser> authenticatedUsers;

   private final Map<String, String> autoNickLinkCheck;

   private static final UserCentral INSTANCE = new UserCentral();

   public static final UserCentral getInstance() {
	  return INSTANCE;
   }

   private UserCentral() {
	  authenticatedUsers = new HashMap<String, AuthenticatedUser>();
	  mainchanUserList = new ArrayList<String>();
	  mainchanUserPrefixes = new HashMap<String, UserPrefixes>();
	  adminchanUserList = new ArrayList<String>();
	  autoNickLinkCheck = new HashMap<String, String>();
   }

   /**
    * Adds a user to the authenticated users list
    * 
    * @param user
    * @param channel
    */
   public void authenticateUser(final String user, final String channel) {
	  authenticatedUsers.put(user, new AuthenticatedUser(getOnlineUser(user), onlineUserIsAdmin(user, channel)));
   }

   /**
    * Lists the users in the admin channel
    * 
    * @return
    */
   public List<String> getAdminchanUsers() {
	  return adminchanUserList;
   }

   public Map<String, AuthenticatedUser> getAuthenticatedUsers() {
	  return authenticatedUsers;
   }

   public Map<String, String> getAutoNickLinkCheck() {
	  return autoNickLinkCheck;
   }

   /**
    * Lists all the nicks a certain user is currently using and is connected to IRC with
    * 
    * @param user
    * @return
    */
   public List<String> getCurrentNicks(final String user) {
	  List<String> out = new LinkedList<String>();
	  for (final Entry<String, AuthenticatedUser> e : authenticatedUsers.entrySet()) {
		 if (e.getValue().getMainNick().equals(user)) {
			out.add(e.getKey());
		 }
	  }
	  return out;
   }

   public Map<String, UserPrefixes> getMainChanUserPrefixes() {
	  return mainchanUserPrefixes;
   }

   /**
    * Lists the users in the main channel
    * 
    * @return
    */
   public List<String> getMainchanUsers() {
	  return mainchanUserList;
   }

   /**
    * Returns the main nick of a user that is currently online and authenticated. Returns an empty string otherwise
    * 
    * @param nick
    * @return
    */
   public String getOnlineUser(final String nick) {
	  if (authenticatedUsers.containsKey(nick))
		 return authenticatedUsers.get(nick).getMainNick();
	  return "";
   }

   /**
    * Returns the UserPrefixes for the specified user
    * 
    * @param user
    * @return
    */
   public UserPrefixes getUserPrefix(final String user) {
	  if (mainchanUserPrefixes.containsKey(user))
		 return mainchanUserPrefixes.get(user);
	  return new UserPrefixes();
   }

   public void nickUpdate(final String oldNick, final String newNick) {
	  if (getAuthenticatedUsers().containsKey(oldNick)) {
		 AuthenticatedUser current = getAuthenticatedUsers().get(oldNick);
		 String mainNick = current.getMainNick();

		 // checks if the user actually owns the nick and isn't pretending to
		 // be someone else
		 // if the nick isn't used already, it checks if the nick is also
		 // registered with nickserv
		 // only if it is will it get linked
		 if (UserHandler.ownsNick(oldNick, mainNick) && UserHandler.nickIsFree(newNick)) {
			autoNickLinkCheck.put(newNick, mainNick);
			Bot.getInstance().doStatusCheck(newNick);
			new Timer().schedule(new NickChangeTask(newNick), 3000);
		 }
		 getAuthenticatedUsers().put(newNick, current);
		 getAuthenticatedUsers().remove(oldNick);
	  } else { // checking if the user switched to a registered nick and
		 // should be authed
		 Bot.getInstance().doStatusCheck(newNick);
	  }
	  if (getMainchanUsers().contains(oldNick)) {
		 getMainchanUsers().remove(oldNick);
		 getMainchanUsers().add(newNick);
	  }
	  if (getMainChanUserPrefixes().containsKey(oldNick)) {
		 getMainChanUserPrefixes().put(newNick, getMainChanUserPrefixes().remove(oldNick));
	  }
	  if (getAdminchanUsers().contains(oldNick)) {
		 getAdminchanUsers().remove(oldNick);
		 getAdminchanUsers().add(newNick);
	  }
	  BotHandler.updateSilenceList(oldNick, newNick);
   }

   /**
    * Checks whether the specified user is an admin, or if he/she is in the admin channel
    * 
    * @param nick
    * @param chan
    * @return
    */
   public boolean onlineUserIsAdmin(final String nick, final String chan) {
	  if (!"".equals(Main.getBotSettings().getAdminChannel()) && chan.equals(Main.getBotSettings().getAdminChannel()))
		 return true;
	  if (authenticatedUsers.containsKey(nick))
		 return authenticatedUsers.get(nick).isAdmin();
	  return false;
   }

   /**
    * Changes the admin status for a user
    * 
    * @param mainNick
    * @param adminStatus
    */
   public void setOnlineUsersAdminStatus(final String mainNick, final boolean adminStatus) {
	  for (String s : getCurrentNicks(mainNick)) {
		 authenticatedUsers.put(s, new AuthenticatedUser(mainNick, adminStatus));
	  }
   }

   /**
    * Adds or removes a prefix to the specified UserPrefixes object
    * 
    * @param up
    * @param plus
    * @param prefix
    * @return
    */
   public UserPrefixes setUserPrefix(final UserPrefixes up, final boolean plus, final String prefix) {
	  if ("q".equals(prefix) || "~".equals(prefix)) {
		 up.isOwner = plus;
	  } else if ("a".equals(prefix) || "&".equals(prefix)) {
		 up.isSop = plus;
	  } else if ("o".equals(prefix) || "@".equals(prefix)) {
		 up.isOp = plus;
	  } else if ("h".equals(prefix) || "%".equals(prefix)) {
		 up.isHalfOp = plus;
	  } else if ("v".equals(prefix) || "+".equals(prefix)) {
		 up.hasVoice = plus;
	  }
	  return up;
   }

   /**
    * Un-authenticates a user
    * 
    * @param user
    */
   public void unauthenticateUser(final String user) {
	  authenticatedUsers.remove(user);
   }
}
